#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

#include <functional>
#include <chrono>
#include <string_view>
#include <vector>
#include <string>
#include <cstdint>

namespace NahidaProject {
	namespace HTTPRequest {
		namespace Implement {
			static class __declspec(dllexport) Initializer {
			public:
				Initializer() noexcept {
					WSADATA wsaData;
					if (WSAStartup(MAKEWORD(1, 1), &wsaData)) {

					}
				}
				~Initializer() noexcept {
					WSACleanup();
				}
			} Initializer;

			class __declspec(dllexport) TCPSocket {
			public:
				TCPSocket() noexcept;
				~TCPSocket() noexcept;

				bool Connect(const std::string& host, std::uint16_t port) noexcept;
				bool Close() noexcept;

				bool Send(const std::byte* data, std::size_t len) noexcept;
				bool Receive(std::byte* data, std::size_t len) noexcept;
			private:
				SOCKET rawSocket;
			};
		}

		enum class __declspec(dllexport) ContentType { formUrlencoded, json, xml, text };

		class __declspec(dllexport) AsyncHTTPRequest {
		public:
			using CallBack = std::function<void(const std::string& response)>;

			AsyncHTTPRequest() noexcept;

			std::string GetHeader(const std::string& name) const noexcept;
			void SetHeader(const std::string& name, const std::string& value) noexcept;
			void RemoveHeader(const std::string& name) noexcept;
			void ClearHeaders() noexcept;
			void Get(std::string_view url, const CallBack& callback) noexcept;
			void Post(std::string_view url, CallBack callback, ContentType content_type, const std::string& data) noexcept;
			bool Server() noexcept;

		private:
			Implement::TCPSocket socket;
			std::chrono::time_point<std::chrono::system_clock> packetTimeout;
			std::vector<std::byte> responseBuffer;
			std::string request;
			std::string response;
			std::unordered_map<std::string, std::string> headers;
			bool readyToReceive;
			CallBack callback;

			bool ParseURL(std::string_view url, std::string& host, std::string& path) const noexcept;
			std::string URLEncode(const std::string& data) const noexcept;
			std::string ToHEX(unsigned char c) const noexcept;
		};
	}
}